P/Invoke的全称是Platform Invoke (平台调用),它实际上是一种函数调用机制通 过P/Invoke我们就可以调用非托管DLL中的函数。
P/Invoke依次执行以下操作:
- 查找包含该函数的非托管DLL
- 将该非托管DLL加载到内存中
- 查找函数在内存中的地址并将其参数按照函数的调用约定压栈
- 将控制权转移给非托管函数

由于项目需要用到了C#代码调用C++的动态库,期间遇到了参数封装与传送的问题,记录于此。
- 对于不对称的结构体数组,最好定义紧凑型排列以避免编译器自动做字节对齐。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential, CharSet = System.Runtime.InteropServices.CharSet.Ansi, Pack=1)] public struct SRawStockDict { public uint m_dwStockID; public byte m_cMarket; public byte m_cType; [System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.ByValTStr, SizeConst = 23)] public string m_cCode; [System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.ByValTStr, SizeConst = 23)] public string m_cName; [System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.ByValTStr, SizeConst = 31)] public string m_cNameEx; public byte m_cFlag; public uint m_dwObjID; public byte m_cOptType; }
|
如上,若不加Pack=1。那么用Marshal.Sizeof(typeof(SRawStockDict))获取到的大小是92。加上Pack=1之后得到的大小就是89,这样在传入被调用的C++函数中不会出错。
- 若导出的C++函数中有指向结构体数组的指针,并且该参数既会作为输入也会作为输出。那么类型匹配如:
1 2 3 4
| C++ SRawStockDict *pNew C# [In, Out] SRawStockDict[] pNew
|
对于SRawStockDict *&pNew 这种特殊的情况 ,若有必要的话可以把C++函数再封装一层,在内部再做一次赋值。同时调用的时候就做好空间申请。
1 2 3 4 5
| SRawStockDict[] sRawStockDict = new SRawStockDict[20000]; for (int i = 0; i < sRawStockDict.Length; i++) { sRawStockDict[i] = new SRawStockDict(); }
|
- char[ ]可直接用byte[ ]作为匹配输入。
最后,推荐一个专门用来做P/Invoke参数及类型转换的工具。
P/Invoke Interop Assistant GUI Tool